#pragma once

#include <string>

#include <boost/asio.hpp>
#include <tbb/atomic.h>
#include <tbb/tick_count.h>
#include <tbb/tbb_thread.h>


/// Keeps information about server and provides it in useful format.
class ServerInfo
{
public:
	/// Detects tries to connect to server and detect valid endpoint.
	/// In case of error throws std::exception().
	ServerInfo(const std::string &hostName, unsigned short port = 80);

	/// Returns valid server endpoint.
	//asio::ip::tcp::socket::endpoint_type &GetEndPoint() const { return _address; }

	/// Returns ip address of the server (e.g. "127.0.0.1").
	boost::asio::ip::address GetIpAddress() const { return _ip; }

	//Returns port for connections (e.g. 80).
	unsigned short GetPort() const { return _port; }

private:
	boost::asio::ip::tcp::socket::endpoint_type _address;
	boost::asio::ip::address _ip;
	unsigned short _port;
};

/// Simple logger, which stores perfomance information to xml.
class XmlLogger
{
public:
	/// Creates logger, which will be storing events to given file.
	/// "testTime" defines expected time of testing (for average values determination).
	XmlLogger(const char *filename, double testTime);
	
	/// Creates record for given statistics.
	void AddRecord(size_t writes, size_t connects, size_t startedRequests, size_t succeededRequests,
		                  size_t failedRequests, size_t pingPongRequests, double elapsedTime);

	~XmlLogger();

private:
	FILE *_f;
	size_t _totalRequests;
	double _totalTime;
	double _testTime;
};

/// Keeps statistics about communication with server.
/// This class is thread safe.
class Stats
{
private:
	Stats();

public:
	/// Registers information about started request
	void RegisterStartedRequest() { ++_startedRequests; }

	/// Registers information about Ping Pong request
	void RegisterPingPongRequest() { ++_pingPongRequests; }

	/// Registers information about completed connect
	void RegisterConnect() { ++_connects; }

	/// Registers information about comleted write
	void RegisterWrite() { ++_writes; }

	/// Registers information about performed request. Should be called by connections.
	void RegisterRequest(bool IsSucceeded, tbb::tick_count::interval_t Duration);

	/// Returns current information.
	void GetInfo(size_t &writes, size_t &connects, size_t &StartedRequests,
				 size_t &SucceededRequests, size_t &FailedRequests, size_t &PingPongRequests,
				 double &TotalTime, bool ShouldClear = false);

	/// Creates new thread, which will print statistics during "time" seconds.
	/// If "shouldTerminate" is setted, than new thread kills application (after "time" seconds).
	static void StartStatsPrinter(const char *filename, double time, bool shouldTerminate = false);

	//Returns reference to instance.
	static Stats *GetRef() { return _ref; }

private:
	/// Entry point for printer thread.
	static void entryPoint(const char *filename, std::pair<double, bool> params);

	static Stats *_ref;

	tbb::atomic<size_t> _succeededRequests;
	tbb::atomic<size_t> _failedRequests;
	tbb::atomic<size_t> _pingPongRequests;
	tbb::atomic<size_t> _elapsedTime;				// contains time as second_count * 1 000 000
	tbb::atomic<size_t> _startedRequests;
	tbb::atomic<size_t> _writes;
	tbb::atomic<size_t> _connects;
	std::vector<tbb::tbb_thread *> _startedThreads;
};
